package gates;
import java.awt.Graphics;
import java.util.*;

import master.*;


/**
 * 
 * A Toffoli Gate or a controlled-controlled-not gate
 *	flips the target bit iff both control bits are set to one		
 *	used mostly in the Quantum Adder implementation 
 *
 * @author Oliver Wiese
 *
 */
public class ToffoliGate extends QuantumGate{

	public static IllegalArgumentException GateException =
			new IllegalArgumentException("applying wrong type of gate to register");

	int control1,control2,target;

	/**
	 * 
	 * @param control1 - first control bit
	 * @param control2 - second control bit
	 * @param target - target bit
	 */
	public ToffoliGate(int control1, int control2, int target){
		this.control1 = control1+1;
		this.control2 = control2+1;
		this.target = target+1;
	}

	/**
	 * acts with the Toffoli Gate on the given register
	 * @param register
	 * 
	 */
	public void actOnReg(QuantumRegister register) {

		List<Integer> mem = new LinkedList<Integer>(); // list to hold states

		for (int t = 0; t < register.getDimension(); t++) { // loop over all basis states
			// if the bits (j1-1) and (j2-1) are not met by t, then go on...


			//compare if the current state has a 1 where the first control bit has a 1 and store the control state 
			int a = (t & power2(control1-1));
			//compare for the second bit and store the control state
			int b = (t & power2(control2-1));
			//sum of the 2 control bit states
			int c = power2(control1 - 1) + power2(control2 - 1);

			// if the control bits are not met in the current state the continue the search 
			if ( a + b < c ) {
				continue;
			}
			// if they are met collect all states that have a zero where the target bit is
			int e = t - power2(target-1);
			if (!mem.contains(e)) {
				mem.add(t);
			}
		}


		ComplexNumber temp;
		// exchange the stored indices:
		int t2;
		for ( int t : mem) { // loop over the collected states

			// find state that has only the target bit changed to 1
			if(t + power2(target-1) < register.getDimension()) {
				t2 = t + power2(target-1); 
			}else{
				t2 = t - power2(target-1);
			}
			// exchange the coefficients of the states that are affected
			temp = register.getCoefficient(t);
			if (t2<0)
				System.out.println("" + t2);
			register.setCoefficient(register.getCoefficient(t2), t);
			register.setCoefficient(temp, t2);

		}
	}

	/**
	 * shifts an integer left by one
	 * @param n
	 * @return 2^n
	 */
	public int power2( int n ) {
		return 1 << n;
	}

	/**
	 * prints out the register as states with coefficients
	 * used only for testing
	 * @param reg
	 */
	public static void printOut(QuantumRegister reg){
		for (int n=0;n<reg.getDimension();n++){

			System.out.println(" " + reg.getCoefficient(n) + "|" + Integer.toBinaryString(n) + "("+ n +")>");
		}
		System.out.println("");
	}



	/**
	 * Returns a list with all the qBits the register acts on
	 */
	public int[] getActingBits() {
		int[] actingBits = new int[3];
		actingBits[0] = control1;
		actingBits[1] = control2;
		actingBits[2] = target;
		return actingBits;
	}

	/**
	 * code snippet used to test the gate
	 */
	public static void main(String[] args){
		QuantumRegister reg = new QuantumRegister(5);
		QuantumGate tgate = new ToffoliGate(4, 3, 1);
		reg.setToEigenstate(24);
		printOut(reg);
		tgate.actOnReg(reg);
		printOut(reg);

	}
}
